#include <stdio.h>
#include "ctypes.h"

#ifdef MPE
#define LWPF_UNITS U(MVSTD)
#include "lwpf2.h"
#define RPCC "rtc %0\n\t"
#endif
#ifdef CPE
#define RPCC "rcsr %0, 4\n\t"
#endif
static inline long rpcc(){
  long x;
  asm volatile(RPCC : "=r"(x));
  return x;
}

#ifdef CPE
#include <slave.h>
#include <simd.h>
#include <dma.h>
#define DMA_FAST
//#define DMA_COUNT_BYTES
#include "dma_macros.h"
#include "swcache.h"
#include "/home/export/online1/swmore/opensource/cal/cal.h"
#define YBLK 128
#define COLBLK 128
#define ABLK 256
static inline long GLD(){
  long x;
  asm volatile("rcsr %0, 8\n\t": "=r"(x));
  return x;
}
#define LWPF_KERNELS K(ALL) K(RLOOP) K(CLOOP) K(CHADDR) K(FETCH)
#define LWPF_UNIT U(MVSTD)
#include "lwpf2.h"
/* #define lwpf_start(x) */
/* #define lwpf_stop(x) */
/* #define lwpf_enter(x) */
/* #define lwpf_exit(x) */
void xmatvec_std(matvec_param_t *gl_pm) {
  lwpf_enter(MVSTD);
  lwpf_start(ALL);
  dma_init();
  matvec_param_t pm;
  pe_get(gl_pm, &pm, sizeof(matvec_param_t));
  dma_syn();
  int n = pm.nrow;
  sreal *gl_ycoefs = pm.ycoefs;
  sreal *xcoefs = pm.xcoefs;
  sreal *gl_acoefs = pm.acoefs;
  sreal beta = pm.beta;
  lint *gl_arowoff = pm.arowoff;
  lint *gl_acols = pm.acols;
  sreal ycoefs[YBLK];
  lint arowoff[YBLK + 1];
  sreal acoefs[ABLK];
  lint acols[ABLK];
  //if (_MYID) return;
#define XCOEFS_CACHE xcoefs, sreal, 3, 4, 8
  SWCACHE_INIT(XCOEFS_CACHE);
  long gld = 0;
  long chsum = 0, tmp;
  lwpf_start(RLOOP);
  for (int rowst = _MYID * YBLK; rowst < n; rowst += 64 * YBLK) {
    int rowed = rowst + YBLK;
    if (rowed > n) rowed = n;
    int rowsz = rowed - rowst;
    //cal_locked_printf("%d %d %d %d\n", _MYID, rowst, rowed, rowsz);
    pe_get(gl_ycoefs + rowst, ycoefs, rowsz * sizeof(sreal));
    pe_get(gl_arowoff + rowst, arowoff, rowsz * sizeof(lint));
    arowoff[rowsz] = gl_arowoff[rowed];
    dma_syn();
    lwpf_start(FETCH);
    int astl = arowoff[0];
    int aedl = astl + ABLK;
    int aed = arowoff[rowsz];
    if (aedl > aed) aedl = aed;
    pe_get(gl_acoefs + astl, acoefs, (aedl - astl) * sizeof(sreal));
    pe_get(gl_acols + astl, acols, (aedl - astl) * sizeof(lint));
    dma_syn();
    lwpf_stop(FETCH);
    lwpf_start(CLOOP);
    for (int row = rowst; row < rowed; row ++){
      int roff = row - rowst;
      sreal sum = beta * ycoefs[roff];
      for (lint i = arowoff[roff]; i < arowoff[roff + 1]; i ++) {
        if (i >= aedl) {
          lwpf_start(FETCH);
          astl = aedl;
          aedl = astl + ABLK;
          if (aedl > aed) aedl = aed;
          pe_get(gl_acoefs + astl, acoefs, (aedl - astl) * sizeof(sreal));
          pe_get(gl_acols + astl, acols, (aedl - astl) * sizeof(lint));
          dma_syn();
          lwpf_stop(FETCH);
        }
        lint ioff = i - astl;
        int idx = acols[ioff];
        sreal *xcoef;
        SWCACHE_TADDR(xcoef, XCOEFS_CACHE, idx);
        sum += acoefs[ioff] * (*xcoef);//xcoefs[acols[ioff]];
        //sum += acoefs[ioff] * (*xcoef);//xcoefs[acols[ioff]];
        //sum += acoefs[ioff] * xcoefs[acols[ioff]];
      }
      ycoefs[roff] = sum;
    }
    lwpf_stop(CLOOP);
    pe_put(gl_ycoefs + rowst, ycoefs, rowsz * sizeof(sreal));
    dma_syn();
  }
  lwpf_stop(RLOOP);
  lwpf_stop(ALL);
  lwpf_exit(MVSTD);
  //cal_locked_printf("%d %ld %ld %ld %ld\n", _MYID, bytecount_shadow[0], bytecount_shadow[1], bytecount_shadow[8], bytecount_shadow[9]);
}
#endif
#ifdef MPE
void xmatvec_std(matvec_param_t *pm){
  int n = pm->nrow;
  sreal *ycoefs = pm->ycoefs;
  sreal *xcoefs = pm->xcoefs;
  sreal *acoefs = pm->acoefs;
  sreal beta = pm->beta;
  lint *arowoff = pm->arowoff;
  lint *acols = pm->acols;
  for (int row = 0; row < n; ++ row) {
    sreal sum = beta * ycoefs[row];
    for (lint i = arowoff[row]; i < arowoff[row + 1]; ++i) {
      sum += acoefs[i] * xcoefs[acols[i]];
    }
    ycoefs[row] = sum;
  }
}
extern void slave_xmatvec_std();
#include <athread.h>
void matvec_std_sw(matvec_param_t *pm) {
  perf_config_t conf;
  conf.pcr0 = PC0_CNT_INST;
  conf.pcr1 = PC1_CNT_LDM_ACCESS;
  conf.pcr2 = PC2_CNT_ICACHE_MISS;
  conf.pcrc = PCRC_ALL;

  if (!athread_idle()) {
    athread_init();
    lwpf_init(&conf);
  }
  //printf("%p\n", pm->xcoefs);
  long start = rpcc();
  //xmatvec_std(pm);
  athread_spawn(xmatvec_std, pm);
  athread_join();
  long end = rpcc();
  printf("matvec takes: %ld\n", end - start);
  lwpf_report_summary_wide(stdout, &conf);
  exit(1);
}
#endif
